/**
   \file  threaded_image_set_processor.hh
   \brief Defines a class for processing training/testing images in
   multiple threads.

   libgist uses an STL map to organize its input images into different
   categories. The keys of this map are the category labels (strings),
   which should be provided by libgist's clients. The values
   corresponding to each of the category labels are containers (STL
   lists, vectors, etc.) of image related data (such as grids of SIFT
   descriptors and so on).

   In the context of libgist, we refer to the above data structure, i.e.,
   a map of container of image data, as an "image set." Often, various
   operations on image sets can be parallelized. This file defines a
   class that encapsulates the details of processing an image set using
   multiple threads.
*/

/*
   This file is part of libgist.

   libgist is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2 of the License, or (at your
   option) any later version.

   libgist is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with libgist; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/

/*
   REVISION HISTORY

   $HeadURL: http://libgist.googlecode.com/svn/branches/wu_dev/src/threaded_image_set_processor.hh $
   $Id: threaded_image_set_processor.hh 78 2011-10-01 20:29:21Z libgist@gmail.com $
*/

#ifndef GIST_THREADED_IMAGE_SET_PROCESSOR_DOT_HH
#define GIST_THREADED_IMAGE_SET_PROCESSOR_DOT_HH

//------------------------------ HEADERS --------------------------------

// Boost
#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>

// Standard C++
#include <map>
#include <string>
#include <stdexcept>

//----------------------- NAMESPACE DEFINITION --------------------------

namespace gist {

//------------------------- CLASS DEFINITION ----------------------------

/**
   \brief A class for processing image sets in parallel.

   In the context of libgist, an image set refers to a map of containers
   of image related data structures indexed by strings. The string
   indices are taken to be image category labels whereas the map's values
   are expected to be containers (usually STL lists or vectors) of
   arbitrary data structures.

   Thus, an image set is a categorized, algorithm and task specific data
   store. Often, various operations on this input data can be
   parallelized. This class encapsulates the details of launching
   multiple threads, accessing the contents of the image set in a
   thread-safe manner, and then passing the category label and image
   related data structure to a client-supplied processing function.

   Since different algorithms and tasks will have different
   representational requirements for the contents of the map, this class
   is templated on the container type that is expected to comprise the
   map's values. Usually, this image list type will be either an STL
   list or an STL vector.

   However, the image_list template type need not be an STL list or
   vector. Clients may use other container types as long as those
   containers provide an iterator interface and the other familiar
   aspects of STL containers.

   To illustrate the general usage pattern for this class, let us say we
   want to load a bunch of categorized images in parallel. For this
   purpose, we have a map of vectors of file names (i.e., strings), a
   function or function object that will load an image given its file
   name, and another function or function object that will report an
   error in case the image could not be loaded. Here is what the code
   might look like:

   \code

      typedef std::vector<std::string> image_list ;
      std::map<std::string, image_list> images ;

      // somehow get a categorized list of image file names and then:
      threaded_image_set_processor<image_list>(images).
         run(N, load_func, error_func) ;

   \endcode

   In the above code snippet, we load images in parallel by creating a
   temporary threaded_image_set_processor, passing it the categorized
   image file names and then invoke the run() method to launch N threads
   to load the images. The load_func and error_func callables should be
   defined appropriately by clients. In the above example, load_func will
   be passed two parameters, both strings; the first will be the image
   category label and the second the image file name. error_func will be
   passed a reference to an exception object.

   The calling function will block in its call to the temporary
   threaded_image_set_processor's run method. Once all the threads are
   done, the run method will finish, which, in turn, will lead to the
   temporary threaded_image_set_processor object going out of scope and
   cleaning up whetever it did to create the multiple threads.

   For more concrete usage examples, see the SIFT grid map to matrix
   conversion operation in lazebnik.cc and the exec() function in the
   libgist driver program's train.cc module.
*/
template<typename image_list>
class threaded_image_set_processor {
   /// An image set is a map of containers of image related data
   /// structures indexed by strings. The keys of this map are the image
   /// categories and its values are the lists of image related data
   /// corresponding to those categories.
   ///
   /// Note that this class does not care what the image set actually
   /// holds. Thus, the "image related data" could be file names, RGB
   /// arrays, SIFT descriptor grids, whatever.
   //@{
   typedef std::map<std::string, image_list> image_set ;
   //@}

   /// Client objects/functions are expected to construct an image set
   /// using some concrete types for the image related data structure and
   /// the list type container held by the image set. They should then
   /// pass a reference to the image set they build to this class to have
   /// it processed in multiple threads.
   /// \return
   const image_set& m_image_set ;

   /// In order to process an image set, we will have to iterate over its
   /// elements. This requires two iterators: one for the map and the
   /// other for the container of image related data.
   //@{
   typedef typename image_set ::const_iterator map_iterator ;
   typedef typename image_list::const_iterator img_iterator ;
   map_iterator m_cat ;
   img_iterator m_img ;
   //@}

   /// We use a thread group to manage all the image set processing
   /// threads.
   /// \return
   boost::thread_group m_threads ;

   /// This mutex ensures that the different threads don't step over each
   /// others' toes while accessing the image set (via the map and img
   /// iterators).
   /// \return
   boost::mutex m_mutex ;

   // Prevent copy and assignment
   threaded_image_set_processor(const threaded_image_set_processor&) ;
   threaded_image_set_processor& operator=(const threaded_image_set_processor&);

public:
   /**
      \brief Initialize the threaded image set processor.
      \param s The image set to be processed.

      To instantiate the threaded image set processor, clients must
      supply the image set to be processed. As mentioned earlier, in this
      context, an image set refers to an STL map of STL-like containers
      of arbitrary image related data structures indexed by strings. The
      keys of this map are the different image categories and its values
      are the lists (containers) of client-specific image related data
      corresponding to the categories.
   */
   threaded_image_set_processor(const image_set& s) ;

   /**
      \brief Launch the multiple threads that will process the image set.
      \param n Number of threads to create.
      \param f The image processing function.
      \param x Exception handler.

      This method creates the multiple threads that will process the
      image set. Note that the calling thread will block in this function
      until all the image set processing threads are done.

      As mentioned earlier, this class only takes care of the details of
      launching and managing multiple threads in order to parallelize the
      image set processing procedure but does not implement the actual
      processing itself, which remains the responsibility of clients.
      Thus, this method expects to be passed a callable (i.e., function
      or function object) that will take care of processing an image.

      This callable will be passed two arguments. The first argument will
      be a string specifying the image category. The second one is the
      image related data. The client-supplied callable can use these two
      arguments in whatever way it sees fit.

      Note that both the arguments to the image processing function,
      i.e., the callable described above, are passed as const references.

      In addition to the image processing function, this method also
      expects to be passed another callable, viz., x, which is an
      exception handler. This callable will be invoked when a thread
      catches an exception. The exception handler will be passed a
      reference to the exception object. After the exception handler is
      done, the thread will exit.
   */
   template<typename func, typename exception_handler>
   void run(int n, func f, exception_handler x) ;


   /**
      \brief Launch the multiple threads that will process the image set.
      \param n Number of threads to create.
      \param f The image processing function.

      This method creates the multiple threads that will process the
      image set. Note that the calling thread will block in this function
      until all the image set processing threads are done.

      As mentioned earlier, this class only takes care of the details of
      launching and managing multiple threads in order to parallelize the
      image set processing procedure but does not implement the actual
      processing itself, which remains the responsibility of clients.
      Thus, this method expects to be passed a callable (i.e., function
      or function object) that will take care of processing an image.

      This callable will be passed two arguments. The first argument will
      be a string specifying the image category. The second one is the
      image related data. The client-supplied callable can use these two
      arguments in whatever way it sees fit.

      Note that both the arguments to the image processing function,
      i.e., the callable described above, are passed as const references.

      Normally, when a Boost.Thread encounters an exception, it
      terminates the application. Often, that is an undesirable response
      and we should allow clients to specify exactly how they would like
      to respond to exceptions by providing their own exception handlers.

      Nonetheless, there are times when clients do not wish to bother
      with supplying a custom exception handler. This version of the run
      method caters to that need by supplying a default handler that does
      nothing. Thus, if a thread encounters an exception, it will
      silently exit, i.e., the thread will terminate but the application
      won't.
   */
   template<typename func>
   void run(int n, func f) {run(n, f, nop) ;}

private:
   // Do-nothing thread exeption handler
   static void nop(const std::exception&){}

   /**
      \brief The image set processing thread function.
      \param tr The threaded_image_set_processor.
      \param fn The image processing function.
      \param ex The exception handler.

      This is the thread function for each of the image set processing
      threads. It expects to be passed a pointer to the
      threaded_image_set_processor object so that it can get at the image
      set, mutex, etc.

      Additionally, it expects two callables: one to actually process each
      "image" in the image set and the other to handle exceptions.
   */
   template<typename func, typename exh>
   static void
   process_image_set(threaded_image_set_processor* tr, func fn, exh ex) ;

public:
   /// Clean-up.
   ~threaded_image_set_processor() ;
} ;

//-------------------- NON-INLINE MEMBER FUNCTIONS ----------------------

// Initialization
template<typename image_list>
threaded_image_set_processor<image_list>::
threaded_image_set_processor(const threaded_image_set_processor::image_set& s)
   : m_image_set(s), m_cat(m_image_set.begin())
{
   if (m_cat != m_image_set.end())
      m_img = m_cat->second.begin() ;
}

// Launching multiple threads
template<typename image_list>
template<typename func, typename exh>
void threaded_image_set_processor<image_list>::run(int n, func f, exh ex)
{
   if (n < 1)
      n = 1 ;
   for (int i = 0; i < n; ++i)
      m_threads.add_thread(new boost::thread(process_image_set<func, exh>,
                                             this, f, ex)) ;
   m_threads.join_all() ;
}

// Processing the image set
template<typename image_list>
template<typename func, typename exh>
void
threaded_image_set_processor<image_list>::
process_image_set(threaded_image_set_processor* tr, func f, exh ex)
{
   try
   {
      const std::string* cat ;
      const typename image_list::value_type* img ;
      for(;;)
      {
         // Local block to automatically acquire and release mutex
         {
            boost::lock_guard<boost::mutex> g(tr->m_mutex) ;
            if (tr->m_cat == tr->m_image_set.end())
               return ;
            while (tr->m_img == tr->m_cat->second.end()) {
               ++tr->m_cat ;
               if (tr->m_cat == tr->m_image_set.end())
                  return ;
               tr->m_img = tr->m_cat->second.begin() ;
            }
            cat = &( tr->m_cat->first) ;
            img = &(*tr->m_img) ;
            ++tr->m_img ;
         }
         // Pass image category and image related data to client-supplied
         // processing function.
         f(*cat, *img) ;
      }
   }
   catch (std::exception& e)
   {
      ex(e) ; // pass exception to client-supplied handler and exit thread
   }
}

// Clean-up
template<typename image_list>
threaded_image_set_processor<image_list>::
~threaded_image_set_processor()
{}

//-----------------------------------------------------------------------

} // end of namespace encapsulating this file's definitions

#endif

/* So things look consistent in everyone's emacs... */
/* Local Variables: */
/* indent-tabs-mode: nil */
/* End: */
